<?php
/**
 * appRain CMF
 *
 * LICENSE
 *
 * This source file is subject to the MIT license that is bundled
 * with this package in the file LICENSE.txt.
 * It is also available through the world-wide-web at this URL:
 * http://www.opensource.org/licenses/mit-license.php
 * If you did not receive a copy of the license and are unable to
 * obtain it through the world-wide-web, please send an email
 * to license@apprain.com so we can send you a copy immediately.
 *
 * @copyright  Copyright (c) 2010 appRain, Team. (http://www.apprain.com)
 * @license    http://www.opensource.org/licenses/mit-license.php MIT license
 *
 * HELP
 *
 * Official Website
 * http://www.apprain.com/
 *
 * Download Link
 * http://www.apprain.com/download
 *
 * Documents Link
 * http ://www.apprain.com/docs
 * 
 * This core resource is developer by Reazaul Karim, reazulk@gmail.com
 *
 */

class appRain_Base_Modules_Universal_Installer extends appRain_Base_Objects
{
    const OVERWRITE = false;
    const SUPPORTED_EXT = 'zip';
    const UNIVERSAL_DEFANATION_FILE_NAME =  'installer.xml';
    const ADVANCED = 'advanced';
    const SIMPLE   = 'simple';
    public $restrictedPath = array(
        array('path'=>'/webroot/uploads/filemanager'),
        array('path'=>'/webroot/addons'),
        array('path'=>'/webroot/componentroot'),
        array('path'=>'/webroot/images'),
        array('path'=>'/webroot/install','mode'=>'strict'),
        array('path'=>'/webroot/js'),
        array('path'=>'/webroot/themeroot'),
        array('path'=>'/webroot/themeroot/admin','mode'=>'strict'),
        array('path'=>'/webroot/uploads'),
        array('path'=>'/webroot'),
        array('path'=>'/component'),
        array('path'=>'/development/cache','mode'=>'strict'),
        array('path'=>'/development/controllers'),
        array('path'=>'/development/definition','mode'=>'strict'),
        array('path'=>'/development/helper'),
        array('path'=>'/development/models'),
        array('path'=>'/development/plugin'),
        array('path'=>'/development/view'),
        array('path'=>'/development/view/system','mode'=>'strict'),
        array('path'=>'/development'),
        array('path'=>'/apprain','mode'=>'strict')
    );
    public $errors = Array();    
    private $mode  = 'auto';
    private $source_auto_delete = true;
    private $resourcepath = "";
    private $definationPath = "";
    private $defaultinstallationpath = "";
    private $tmp_path = null;
    private $xmlObj = Array();
    private $name = "";
    private $isAutoCopy = true;

    private $_ftp = null;
 
    public function upload($fileRes)
    {
        if(!isset($this->tmp_path)){
            $this->tmp_path = DATA;
        }

        $resFileExt = App::Helper('Utility')->getExt($fileRes['name']);
        
        $resName = App::Helper('Utility')->getName($fileRes['name']);
        
        if($resFileExt != self::SUPPORTED_EXT){
            $this->errors[] = "File does not support";
        }
        else if(
            file_exists($this->getDefaultinstallationpath() . DS . $resName)
            ){
            $this->errors[] = "'{$resName}' Component already installed";
        }
        else if(!is_writable($this->tmp_path)){
            $this->errors[] = "Path is no writeable '{$this->tmp_path}'";
		}
        else{
            $dirName = time();
            
            $this->tmp_path = $this->tmp_path . DS . $dirName;
            $this->attachForcePermission($this->tmp_path);

            App::Helper('Utility')
                ->createDir($this->tmp_path,0777);
            
            $fileInfo = App::Load("Helper/Utility")
                ->upload($fileRes, $this->tmp_path . DS );

            $this->setResourcePath($this->tmp_path . DS . $fileInfo['file_name']);           
            $this->isAutoCopy = false;
        }
        
        return $this;
    }
    
    /* INSTALL RESOURCE
       =======================================================================*/
    public function Install()
    {
        if($this->prepare()){
            $this->temporary_extract()
                ->checkDefinition()
                ->executeCommand()
                ->clearResource();
        }
        
        return $this;
    }
    
    private function prepare()
    {
        if(!empty($this->errors)){
            return false;
        }

        if(!isset($this->tmp_path)){
            $this->tmp_path = DATA;
        }

        if(is_bool($this->getSourceAutoDelete())){
            $this->source_auto_delete = $this->getSourceAutoDelete();
        }

        if($this->getResourcePath()){
            $this->resourcepath = $this->getResourcePath();
        }

        if(!file_exists($this->resourcepath)){
            $this->errors[] = 'Source path not found: ' . $this->getResourcePath();
            return false;
        }
        
        $arr = preg_split('[\/|\\\|\.]',$this->resourcepath);
        $this->name = $arr[count($arr)-2];

        if($this->getDefaultInstallationPath()){
            $this->defaultinstallationpath = $this->getDefaultInstallationPath();
        }

        if(!file_exists($this->defaultinstallationpath)){
            $this->errors[] = 'Default Installation path is not correct: ' . $this->getResourcePath();
            return false;
        }

        $this->attachForcePermission($this->resourcepath);
        $this->attachForcePermission($this->defaultinstallationpath);

        return true;
    }
    
    private function temporary_extract()
    {
        if($this->isAutoCopy){
            $dirName = time();
            $this->tmp_path = $this->tmp_path . DS . $dirName;
            App::Helper('Utility')->createDir($this->tmp_path,0777);
        }

        App::Module('Zip')
           ->setFileName($this->resourcepath)
           ->Extract($this->tmp_path);
        return $this;
    }
    
    private function checkDefinition($isResource=false)
    {
        if($isResource){
            $this->definationPath  = $this->resourcepath . DS . self::UNIVERSAL_DEFANATION_FILE_NAME;
        }
        else {
            $this->definationPath = $this->tmp_path . DS . $this->name . DS . self::UNIVERSAL_DEFANATION_FILE_NAME;
        }
        if(!file_exists($this->definationPath)){
            $this->mode = self::SIMPLE;
        }
        else{
            $this->xmlObj = simplexml_load_file($this->definationPath);
            $this->mode = self::ADVANCED;    
        }

       return $this;
    }   
    
    private function executeCommand()
    {
        if($this->mode == self::SIMPLE){
            $this->attachForcePermission($this->defaultinstallationpath . DS . $this->name);
            App::Helper('Utility')
                ->dirFullCopy(
                    $this->tmp_path . DS . $this->name,
                    $this->defaultinstallationpath . DS . $this->name
                );
        }
        else
        {
            foreach($this->xmlObj->command->copy as $command){
                $src = $this->senitizePath((string) $command->src); 
                $des = $this->senitizePath((string) $command->des,true);
                $this->attachForcePermission($des);
                App::Helper('Utility')->dirFullCopy($src,$des);
            }

            $definition_path = $this->defaultinstallationpath . DS . $this->name . DS . self::UNIVERSAL_DEFANATION_FILE_NAME;

            if(file_exists($definition_path)){
                $this->errors[] = "Already exists : {$definition_path}";
            }
            else{
                $this->attachForcePermission($definition_path);
                
                App::Helper('Utility')
                    ->Copy(
                        $this->definationPath,$definition_path
                    );
            }
        }
        
        return $this;
    }
    
    private function clearResource()
    {
        $this->attachForcePermission($this->tmp_path);
        App::Helper('Utility')
            ->dirFullRemove(
                dirname($this->resourcepath)
            );
    }
    
    private function senitizePath($src="",$isBase=false)
    {
       return ($isBase) 
           ? App::Helper('Config')->rootDir($src) 
           : $this->tmp_path . DS . $this->name . $src;
    }

    public function attachForcePermission($path=null)
    {
        if(isset($path) && file_exists($path) && !is_writable($path)){
            $ftpInfo = App::Config()->siteInfo();
            $ftpserver = isset($ftpInfo['ftpserver']) ? $ftpInfo['ftpserver'] : null;
            $ftpusername = isset($ftpInfo['ftpusername']) ? $ftpInfo['ftpusername'] : null;
            $ftppassword = isset($ftpInfo['ftppassword']) ? $ftpInfo['ftppassword'] : null;
            $ftpstatus = isset($ftpInfo['ftpstatus']) ? $ftpInfo['ftpstatus'] : null;
 
            if(
                isset($ftpserver) && isset($ftpusername) && isset($ftppassword) &&
                !strstr(App::Config()->baseUrl(),'localhost') &&  strtolower($ftpstatus) == 'enable'
            ) {
                if(!isset($this->_ftp)){
                     $this->_ftp = App::Module('Ftp');
                     $this->_ftp->SetServer($ftpserver);
                     $this->_ftp->connect();
                     $this->_ftp->login($ftpusername,$ftppassword);     
                     $this->_ftp->SetType(FTP_AUTOASCII);
                     $this->_ftp->Passive(FALSE);
                    
                }
                $this->_ftp->chmod($path,0777);
            }
            else {
                @chmod($path,0777);
            }

            return $path;
        }
    }
    
    /* UNINSTALL RESOURCE
       =======================================================================*/   
    public function Uninstall()
    {
        $this->resourcepath = $this->getResourcePath();
        $this->checkDefinition(true)
             ->Run();
    }
    
    public function Run()
    {
        if($this->mode == self::SIMPLE){
            $this->attachForcePermission($this->resourcepath);
            App::Helper('Utility')
                ->dirFullRemove($this->resourcepath);
        }
        else{
            foreach($this->xmlObj->command->copy as $command){
                $cpath = (string)$command->des;
                if($this->checkpathtodelete($cpath)){
                    $path = $this->senitizePath($cpath,true);
                    if(file_exists($path)){
                        $this->attachForcePermission($path);
                        App::Helper('Utility')->dirFullRemove($path);
                    }
                }
            }
        }
    }

    public function checkpathtodelete($path=null){

        for($i=0;$i<count($this->restrictedPath);$i++){
            
            $mode = isset($this->restrictedPath[$i]['mode']) 
                ? $this->restrictedPath[$i]['mode'] : 'normal';

            if($mode == 'normal'){
                if($this->restrictedPath[$i]['path'] == $path OR $this->restrictedPath[$i]['path'] . DS == $path){
                    return false;
                }
            }
            else {
                if(
                    substr($path,0,strlen($this->restrictedPath[$i]['path'])) == $this->restrictedPath[$i]['path'] 
                    OR 
                    substr($path,0,strlen($this->restrictedPath[$i]['path'] . DS)) == $this->restrictedPath[$i]['path'] . DS
                    ){
                    return false;
                }
            }
        }
        return true;
    }
}